﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace Genuilder.Extensibility
{
	public class ExtensionContext
	{
		internal class GenItemCreation
		{
			public GenItem Child
			{
				get;
				set;
			}
			public GenItem Parent
			{
				get;
				set;
			}
		}

		IGenLogger _GlobalLogger;
		public ExtensionContext(IExtension extension, ExecutionContext executionContext, IGenLogger logger)
		{
			this._Extension = extension;
			_GlobalLogger = logger;
			_Logger = logger.CreateExtensionLogger(_Extension);
			this._ExecutionContext = executionContext;
			_Root = _ExecutionContext._Root.Clone();
			_GenItems = new GenItemCollection(_Root.Flatten().Select(c => new GenItem(c, executionContext.StartInfo, _GlobalLogger)).ToList());
		}


		public GenItem CreateChildItem(string childName)
		{
			childName = ExecutionContext.StartInfo.CreateGeneratedChildName(childName);
			var childSource = _Root.CreateOrResolve(childName);
			var child = new GenItem(childSource, ExecutionContext.StartInfo, _GlobalLogger);
			child.RecreateFile();
			return child;
		}
		private readonly GenItemCollection _GenItems;
		public GenItemCollection GenItems
		{
			get
			{
				return _GenItems;
			}
		}

		private readonly ExecutionContext _ExecutionContext;
		public ExecutionContext ExecutionContext
		{
			get
			{
				return _ExecutionContext;
			}
		}
		private readonly IExtension _Extension;
		public IExtension Extension
		{
			get
			{
				return _Extension;
			}
		}

		ExecutionContext.SourceFile _Root;
		internal void Flush()
		{
			_ExecutionContext._Root = _Root;
		}

		private readonly IExtensionLogger _Logger;
		public IExtensionLogger Logger
		{
			get
			{
				return _Logger;
			}
		}
	}

	public class ExecutionContext
	{
		internal class SourceFile
		{
			public SourceFile()
				: this(null)
			{
			}
			public SourceFile(string name)
			{
				Name = name;
				if(name == null)
					_SourcesByName = new Dictionary<string, SourceFile>();
			}
			private SourceFile Clone(SourceFile parent)
			{
				SourceFile clone = new SourceFile(Name);
				clone.Parent = parent;
				clone.Generated = Generated;
				clone.AddChildren(Children.Select(c => c.Clone(clone)));
				return clone;
			}
			public SourceFile Clone()
			{
				return Clone(null);
			}

			public string Name;
			public List<SourceFile> Children = new List<SourceFile>();
			public SourceFile Parent;
			public bool Generated;

			public void AddChildren(IEnumerable<SourceFile> children)
			{
				foreach(var child in children)
				{
					AddChild(child);
				}
			}

			public IEnumerable<SourceFile> Flatten()
			{
				foreach(var child in Children)
				{
					yield return child;
					foreach(var subChild in child.Flatten())
						yield return subChild;
				}
			}

			public override string ToString()
			{
				return Name;
			}

			public void AddChild(SourceFile child)
			{
				child.Parent = this;
				Children.Add(child);
				Register(child);
			}

			Dictionary<string, SourceFile> _SourcesByName;
			private void Register(SourceFile child)
			{
				if(_SourcesByName == null)
					Parent.Register(child);
				else
					_SourcesByName.Add(child.Name, child);
			}
			private void UnRegister(SourceFile child)
			{
				if(_SourcesByName == null)
					Parent.UnRegister(child);
				else
					_SourcesByName.Remove(child.Name);
			}

			public void RemoveChild(SourceFile child)
			{
				if(Children.Remove(child))
				{
					child.Parent = null;
					UnRegister(child);
				}
			}



			public SourceFile Resolve(string childName)
			{
				if(_SourcesByName == null)
					return Parent.Resolve(childName);
				SourceFile src = null;
				_SourcesByName.TryGetValue(childName, out src);
				return src;
			}

			internal SourceFile CreateOrResolve(string childName)
			{
				var childSource = Resolve(childName);
				if(childSource == null)
				{
					childSource = new ExecutionContext.SourceFile(childName);
					childSource.Generated = true;
				}
				if(childSource.Parent != null && childSource.Parent != this)
					childSource.Parent.RemoveChild(childSource);
				if(childSource.Parent != this)
					this.AddChild(childSource);
				return childSource;
			}
		}


		internal SourceFile _Root;

		public IEnumerable<string> SourceFiles
		{
			get
			{
				return _Root.Flatten().Select(s => s.Name);
			}
		}

		readonly IGenLogger _Logger;
		public ExecutionContext(List<string> sourceFiles, GenEngineStartInfo startInfo, IGenLogger logger)
		{
			if(logger == null)
				throw new ArgumentNullException("logger");
			_StartInfo = startInfo;
			_Logger = logger;
			_Root = new SourceFile();
			_Root.AddChildren(sourceFiles.Select(s => new SourceFile(s)));
			if(startInfo.RunHistory != null)
			{
				var sourcesByName = _Root.Flatten().ToDictionary(o => o.Name);

				foreach(var item in startInfo.RunHistory.GenItems.Where(i => i.Generated))
				{
					var sourceFile = item.ToSourceFile();
					sourcesByName.Add(sourceFile.Name, sourceFile);
					if(item.Parent == null)
						_Root.AddChild(sourceFile);
					else
					{
						if(sourcesByName.ContainsKey(item.Parent))
							sourcesByName[item.Parent].AddChild(sourceFile);
					}
				}
			}
		}

		private readonly GenEngineStartInfo _StartInfo;
		public GenEngineStartInfo StartInfo
		{
			get
			{
				return _StartInfo;
			}
		}

		internal ExtensionContext CreateExtensionContext(IExtension extension)
		{
			return new ExtensionContext(extension, this, _Logger);
		}

		internal RunHistory CreateHistory()
		{
			return new RunHistory()
			{
				GenItems = _Root.Flatten().Select(i => GenItemHistory.CreateFrom(i)).ToList()
			};
		}
	}
}
